package com.asset.dispoal.poi.tools.service;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.asset.dispoal.poi.NumberToChinese;
import com.asset.dispoal.poi.documentation.util.ZipUtil;
import com.asset.dispoal.poi.tools.api.IToolService;
import com.asset.dispoal.poi.tools.dto.CivilComplaintRequest;
import com.asset.dispoal.poi.tools.tool.CenterCellStyleStrategy;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.poi.openxml4j.util.ZipSecureFile;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import tech.mhuang.pacebox.core.exception.BusinessException;
import tech.mhuang.pacebox.core.util.CollectionUtil;
import tech.mhuang.pacebox.core.util.StringUtil;
import tech.mhuang.pacebox.springboot.protocol.Result;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @description:
 * @author: yf
 * @date: 2023-07-12 16:03:05
 */
@Service
public class ToolsServiceImpl implements IToolService {
    @Autowired
    private Environment environment;

    @Override
    public Result createComplaint(CivilComplaintRequest request) {
        List<Map<Integer, Object>> resultList = new CopyOnWriteArrayList<>();
        List<List<String>> headerLists = new CopyOnWriteArrayList<>();
        Map<Integer, Row> rowMap = new ConcurrentHashMap<>();
        Workbook sourceWorkbook;
        int sheetNumber;
        Map<Integer, List> templateMap = new HashMap<>();
        Map<Integer, List> templateHeadMap = new HashMap<>();

        try {
            ZipSecureFile.setMinInflateRatio(-1.0d);
            sourceWorkbook = new XSSFWorkbook(request.getExcelFile().getInputStream());
            Sheet sourceWorkbookSheet = sourceWorkbook.getSheetAt(request.getSheet());
            sheetNumber = sourceWorkbook.getNumberOfSheets();
            for (int i = 0; i < sheetNumber; i++) {
                if (i != request.getSheet()) {
                    ExcelReaderSheetBuilder sheetBuilder = EasyExcel.read(request.getExcelFile().getInputStream()).sheet(i);
                    List<List<Object>> sourceSheetData = sheetBuilder.doReadSync();
                    templateMap.put(i, sourceSheetData);
                    Iterator<Cell> cell = sourceWorkbook.getSheetAt(i).getRow(0).cellIterator();
                    List cellList = new ArrayList<>();
                    while (cell.hasNext()) {
                        Cell cellv = cell.next();
                        List<String> cellList2 = new ArrayList<>();
                        cellList2.add(cellv.getStringCellValue());
                        cellList.add(cellList2);
                    }
                    templateHeadMap.put(i, cellList);
                }
            }
            //行数指针
            AtomicInteger rowIndex = new AtomicInteger(0);

            EasyExcel.read(request.getExcelFile().getInputStream(), new AnalysisEventListener<Map<Integer, Object>>() {
                @Override
                public void invoke(Map<Integer, Object> map, AnalysisContext analysisContext) {
                    int indexRow = rowIndex.addAndGet(1);
                    Row row = sourceWorkbookSheet.getRow(indexRow);
                    rowMap.put(indexRow, row);
                    map.put(1000, indexRow);
                    resultList.add(map);
                }

                @Override
                public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
                    headMap.forEach((k, v) -> {
                        List<String> heads = new ArrayList<>();
                        heads.add(v);
                        headerLists.add(heads);
                    });
                }

                @Override
                public void doAfterAllAnalysed(AnalysisContext analysisContext) {
                }
            }).sheet(request.getSheet()).doRead();


        } catch (IOException e) {
            throw new BusinessException(500, "读取表格数据异常", e);
        }

        Map<String, String> cloumDateFormatMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(request.getCloumChineseDate())) {
            for (String cloumDateFormat : request.getCloumChineseDate()) {
                String[] split = cloumDateFormat.split(",");
                cloumDateFormatMap.put(split[0], split[1]);
            }
        }
        List<Map<String, Object>> resultListLetter = resultList.stream()
                .skip(request.getSkipRows())
                .map(resultMap -> resultMap.entrySet().stream().collect(Collectors.toMap(
                        entry -> colNumToLetter(entry.getKey() + 1),
                        entry -> {
                            if (cloumDateFormatMap.get(colNumToLetter(entry.getKey() + 1)) != null) {
                                if (currentTimeIsFormat(Optional.ofNullable((String) entry.getValue()).orElse(""), cloumDateFormatMap.get(colNumToLetter(entry.getKey() + 1)))) {
                                    return Optional.ofNullable(entry.getValue()).orElse("");
                                } else {
                                    return currentTimeToFormat(Optional.ofNullable((String) entry.getValue()).orElse(""), cloumDateFormatMap.get(colNumToLetter(entry.getKey() + 1)));
                                }
                            } else {
                                return Optional.ofNullable(entry.getValue()).orElse("");
                            }
                        })))
                .collect(Collectors.toList());
        //根据请求将数据分成多少份数
        String cloumShare = request.getCloumShare();
        if (StringUtil.isNotEmptyNull(cloumShare)) {
            String[] batchArray = cloumShare.split(",");
            String cloumStr = batchArray[0];
            int share = Integer.parseInt(batchArray[1]);
            Map<String, List<Map<String, Object>>> orderByCloumMap = resultListLetter.stream()
                    .collect(Collectors.groupingBy(map -> (String) map.get(cloumStr), LinkedHashMap::new, Collectors.toList()));

            List<Map<String, List<Map<String, Object>>>> list = new ArrayList<>();
            int count = 0;
            Map<String, List<Map<String, Object>>> subMap = new LinkedHashMap<>(share);
            for (Map.Entry<String, List<Map<String, Object>>> entry : orderByCloumMap.entrySet()) {
                subMap.put(entry.getKey(), entry.getValue());
                count++;
                if (count == share) {
                    list.add(subMap);
                    subMap = new LinkedHashMap<>();
                    count = 0;
                }
            }
            if (CollectionUtil.isNotEmpty(subMap)) {
                list.add(subMap);
            }

            //表头
            LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
            Configure configure = Configure.builder()
                    .useSpringEL(false)
                    .bind("test3", policy)
                    .build();

            XWPFTemplate template = null;
            try {
                int fen = 1;
                String randomFolder = UUID.randomUUID().toString().replace("-", "");
                Path path = Paths.get(environment.getProperty("file.evidence.url") + "dataTemplateCreate", randomFolder);
                Set<String> cloudSex = new HashSet<>(request.getCloumSex());
                Map<String, String> birthdayMap = new HashMap<>();
                for (String birthday : request.getCloumBirthday()) {
                    String[] split = birthday.split(",");
                    birthdayMap.put(split[0], split[1]);
                }
                AtomicInteger seqIndex = new AtomicInteger(1);
                //进行数据渲染
                for (Map<String, List<Map<String, Object>>> listMap : list) {
                    int seqInt = seqIndex.getAndIncrement();
                    Map<String, Object> renderMap = new HashMap<>();
                    renderMap.put("chineseSeq", NumberToChinese.toChineseNumber(seqInt));
                    List<Map<String, Object>> foreachList = new ArrayList<>();
                    List<Map<String, Object>> tableList = new ArrayList<>();
                    AtomicInteger index = new AtomicInteger(1);
                    listMap.forEach((k, v) -> {
                        int indexInt = index.getAndIncrement();
                        v.get(0).put("chineseSeq", NumberToChinese.toChineseNumber(indexInt));
                        v.get(0).put("index", String.valueOf(indexInt));
                        for (String sexCloum : cloudSex) {
                            v.get(0).put(sexCloum + "sex", sexByIdcard(Optional.ofNullable((String) v.get(0).get(sexCloum)).orElse("")));
                        }
                        for (Map.Entry<String, String> map : birthdayMap.entrySet()) {
                            String key = map.getKey();
                            String value = map.getValue();
                            String idCard = (String) v.get(0).get(key);
                            v.get(0).put(key + "birthday", birthdayByIdcard(idCard + "," + value));
                        }
                        foreachList.add(v.get(0));
                        tableList.addAll(v);
                    });
                    Map<String, List> map2 = new HashMap<>();
                    foreachList.forEach(value -> {
                        value.forEach((k, v) -> {
                            List s = map2.getOrDefault(k, new ArrayList());
                            s.add(v);
                            map2.put(k, s);
                        });
                    });
                    renderMap.put("test2", map2);
                    renderMap.put("test", foreachList);
                    renderMap.put("test3", tableList);

                    try {
                        if (!Files.exists(path)) {
                            Files.createDirectories(path);
                        }
                        String outPath = Paths.get(path.toString(), "填充结果(" + fen + ").docx").toString();
                        String partExcelPath = Paths.get(path.toString(), "生成结果(" + fen + ").xlsx").toString();
                        template = XWPFTemplate.compile(request.getDocxTemplateFile().getInputStream(), configure).render(renderMap);


                        //去掉sex  birthday index
                        tableList.forEach(map ->
                                map.entrySet().removeIf(entry ->
                                        entry.getKey().matches(".*birthday") ||
                                                entry.getKey().matches("ALM") ||
                                                entry.getKey().matches(".*sex") ||
                                                entry.getKey().matches("index") ||
                                                entry.getKey().matches("chineseSeq")));
                        List headerList = headerLists.stream().peek(headers -> {
                            String header = headers.get(0);
                            if (StringUtil.isEmpty(header)) {
                                headers.clear();
                                headers.add("");
                            }
                        }).collect(Collectors.toList());
                        ExcelWriter excelWriter = EasyExcel.write(partExcelPath)
                                .autoTrim(true)
                                .registerWriteHandler(new CenterCellStyleStrategy())
                                .build();

                        for (int i = 0; i < sheetNumber; i++) {
                            if (i == request.getSheet()) {
                                WriteSheet writeSheet = EasyExcel.writerSheet(sourceWorkbook.getSheetName(i)).head(headerList).
                                        build();
                                excelWriter.write(listMapToListlist(tableList), writeSheet);
                            } else {
                                WriteSheet writeSheet = EasyExcel.writerSheet(sourceWorkbook.getSheetName(i)).head(templateHeadMap.get(i)).build();
                                excelWriter.write(templateMap.get(i), writeSheet);
                            }
                        }
                        excelWriter.finish();
                        File file = new File(outPath);
                        if (file.exists()) {
                            file.delete();
                        }
                        template.writeToFile(outPath);
                        fen++;
                    } catch (Exception e) {
                        throw new RuntimeException("模板数据渲染异常", e);
                    }
                }
                if (template != null) {
                    try {
                        template.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
                ZipUtil.compress(path.toString(), null);
                try {
                    deleteDirs(path);
                } catch (Exception ignored) {

                }

                return Result.ok(path + ".zip");
            } finally {
                try {
                    sourceWorkbook.close();
                    if (template != null) {
                        template.close();
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        //        sex  birthday
        return Result.faild();
    }

    private List<List<Object>> listMapToListlist(List<Map<String, Object>> listMap) {
        return listMap.stream()
                .map(map -> new ArrayList<>(map.values()))
                .collect(Collectors.toList());
    }

    /**
     * 将列数转换成字母表达
     *
     * @param num
     * @return
     */
    private String colNumToLetter(int num) {
        StringBuilder result = new StringBuilder();
        char letter;
        while (num > 0) {
            if (num > 26) {
                int remainder = num % 26;
                // 如果余数为0，则表示当前位为Z
                if (remainder == 0) {
                    letter = 'Z';
                    num = num - 1;
                } else {
                    letter = (char) (remainder + 64);
                }
                result.insert(0, letter);
                num = num / 26;
            } else {
                letter = (char) (num + 64);
                result.insert(0, letter);
                num = 0;
            }
        }
        // 返回结果字符串
        return result.toString();
    }


    private void deleteDirs(Path path) {
        try {
            Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    Files.delete(file);
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                    Files.delete(dir);
                    return FileVisitResult.CONTINUE;
                }
            });
        } catch (IOException ignored) {
        }
    }

    private boolean currentTimeIsFormat(String timeStr, String format) {
        try {
            DateTimeFormatter dtf = DateTimeFormatter.ofPattern(format);
            LocalDate date = LocalDate.parse(timeStr, dtf);
            return true;
        } catch (DateTimeParseException e) {
            return false;
        }
    }

    private String currentTimeToFormat(String timeStr, String format) {
        String result = null;
        try {
            String[] patterns = {"yyyy-MM-dd", "yyyy/MM/dd", "yyyy年MM月dd日"};
            Date date = DateUtils.parseDate(timeStr, patterns);
            SimpleDateFormat sdf = new SimpleDateFormat(format);
            result = sdf.format(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return result;
    }

    private String sexByIdcard(String idcard) {
        String gender = "";
        if (StringUtil.isNotEmptyNull(idcard)) {
            int genderDigit = Integer.parseInt(idcard.substring(16, 17));
            if (genderDigit % 2 == 0) {
                gender = "女";
            } else {
                gender = "男";
            }

        }
        return gender;
    }

    private String birthdayByIdcard(String idcard) {
        if (StringUtil.isNotEmptyNull(idcard)) {
            String[] split = idcard.split(",");
            String card = split[0];
            String type = split[1];
            switch (type) {
                case "1":
                    return card.substring(6, 10) + "-" + card.substring(10, 12) + "-" + card.substring(12, 14);
                case "2":
                    return card.substring(6, 10) + "/" + card.substring(10, 12) + "/" + card.substring(12, 14);
                case "3":
                    return card.substring(6, 10) + "年" + card.substring(10, 12) + "月" + card.substring(12, 14) + "日";
            }
        }
        return "";
    }
}
